World = class()

function World:init()
    self.player = Player(6, 6)
    self.level = 0
    self.message = ""
    self:nextLevel()
end

function World:nextLevel()
    self.grid = Grid()
    self.grid:generateLevel(self.player)
    self.player:light(self)

    self.bullets = {}
    self.npcs = {}
    self.patterns = {}

    self.level = self.level + 1
    self.message = "Welcome to level " .. self.level .. "!"

    if self.level == 1 then
        self.message = self.message .. " Press ? for controls."
    end
end

function World:update(key)
    self.grid:update(world, key)

    self.player:update(world, key)

    -- Iterate over things backwards, so that it's easier to remove them
    -- without messing with the iterator.

    for i = #self.bullets, 1, -1 do
        self.bullets[i]:update(world)

        if self.bullets[i].dead then
            table.remove(self.bullets, i)
        end
    end

    for i = #self.npcs, 1, -1 do
        self.npcs[i]:update(world)

        if self.npcs[i].dead then
            table.remove(self.npcs, i)
        end
    end

    for i = #self.patterns, 1, -1 do
        self.patterns[i].pattern:update(world)

        if self.patterns[i].pattern.done then
            -- breaks between patterns
            if self.patterns[i].undead and self.patterns[i].undead > 20 then
                table.remove(self.patterns, i)
            elseif self.patterns[i].undead then
                self.patterns[i].undead = self.patterns[i].undead + 1
            else
                self.patterns[i].undead = 0
            end
        end
    end

    for i = #self.npcs, 1, -1 do
        self.npcs[i]:testDead(self)

        if self.npcs[i].dead then
            table.remove(self.npcs, i)
        end
    end

    self.player:checkBullet(world)
    self.player:checkNPC(world)

    -- TODO: difficulty based on level?
    if #self.patterns < 2 and (love.math.random(1, 50) == 1 or #self.patterns == 0) then
        self:addRandomEdgePattern()
    end

    if #self.npcs < math.min(4, self.level / 2 + 1) then
        self:spawnNPC()
    end
end

function World:draw(text)
    self.grid:draw(text)

    for k, v in pairs(self.bullets) do
        v:draw(text)
    end

    for k, v in pairs(self.npcs) do
        if self.grid[v.y][v.x].lit then v:draw(text)
        end
    end

    self.player:draw(text)
end

function World:addRandomEdgePattern()
    -- Adds a bullet pattern to a random edge that does not already have
    -- a pattern.

    local edges = {'top', 'bottom', 'left', 'right'}
    local pickedEdge = nil
    while #edges do
        local i = love.math.random(1, #edges)
        local edge = edges[i]
        local thisEdgeUsed = false

        for k, pattern in pairs(self.patterns) do
            if pattern.edge == edge then
                thisEdgeUsed = true
                break
            end
        end

        if thisEdgeUsed then
            table.remove(edges, i)
        else
            pickedEdge = edge
            break
        end
    end

    if pickedEdge then
        local factory = EDGE_PATTERNS[love.math.random(1, math.min(self.level + 2, #EDGE_PATTERNS))]
        self.patterns[#self.patterns + 1] = factory(pickedEdge)
    end
end

function World:spawnNPC()
    local npc

    while true do
        npc = Goblin(love.math.random(WIDTH), love.math.random(HEIGHT))
        local a = distanceBetweenPoints(npc, self.player)
        local b = self.grid[npc.y][npc.x]

        if not (b.lit or b.solid) and a > 10 then
            break
        end
    end

    self.npcs[#self.npcs + 1] = npc
end

function World:getFullMessage()
    local text = ''

    if self.player.dead then
        text = text .. self.player.deathMessage
        text = text .. ' You got to level ' .. self.level
        text = text .. ' with ' .. self.player.treasure .. ' treasure.'
        text = text .. ' Press R to play again.'
        return text
    end

    if self.player.treasure > 0 then
        if self.player.treasure == 69 then
            text = text .. '[Treasure: ' .. self.player.treasure .. '. nice] '
        else
            text = text .. '[Treasure: ' .. self.player.treasure .. '] '
        end
    end

    text = text .. self.message

    return text
end
